/* eslint-disable @typescript-eslint/no-this-alias */

import { useMessage } from '@/hooks'
import { addStyleToHead } from '@/utils/style'
import { useSortable } from './sortable'
import { getTsToJs, getInsBwUtils } from './setupLogic'
import { isFunction, cloneDeep, isObject } from 'lodash-es'
import { getCustomComPropDataType, getCustomComPropDefaultValue } from './props'
import {
  h,
  markRaw,
  watch,
  nextTick,
  getCurrentInstance,
  defineComponent,
} from 'vue'

import type { VNode, DefineComponent } from 'vue'
import type {
  BwVueIns,
  BwChildrenNodeCallback,
  BwCustomComDesignClassConstructorModel,
} from './model'
import {
  BwCustomComFrontTypeEnum,
  type BwCustomComStructModel,
  type BwCustomComDesignInfoModel,
} from '@bitwit/base-com-render'

import desiginItem from '../coms/designItem/index.vue'
import designContainer from '../coms/designContainer/index.vue'

export class CustomComDesign {
  protected curComNodes: Array<VNode> = []
  protected curVueIns: Nullable<BwVueIns> = null

  protected frontType: BwCustomComFrontTypeEnum
  protected nodeForVars: Record<string, Array<any>> = {}
  protected designInfo: BwCustomComDesignInfoModel | null
  protected curCustomCom: Nullable<DefineComponent> = null

  constructor(data: BwCustomComDesignClassConstructorModel) {
    this.designInfo = data.designInfo ?? null
    this.frontType = data.front || BwCustomComFrontTypeEnum.pc
  }

  /** 生成实例组件 */
  protected createCom() {
    const self = this
    const curCom = defineComponent({
      name: 'MyCustomComDesign',
      props: self.createComProps(),
      emits: self.createComEmits(),
      // @ts-ignore
      setup(props, { emit, expose }) {
        const curIns = getCurrentInstance()
        // @ts-ignore
        const curProxyIns: BwVueIns = curIns?.proxy as BwVueIns

        let resutl: Record<string, any> = {}
        let curSetupLogic = self.getSetupLogic()
        if (curSetupLogic) {
          try {
            curSetupLogic = getTsToJs(curSetupLogic)
            curSetupLogic = curSetupLogic.replace('export default', 'return ')
            const fn = new Function('bwUtils', curSetupLogic)
            const fnResutl = fn(getInsBwUtils({ params: {} }))
            const setupFun = fnResutl.setup
            if (isFunction(setupFun)) {
              resutl = setupFun(props, emit, expose)
            }
          } catch {
            console.error('数据组件逻辑定义错误，请检查！')
          }
        }
        watch(
          () => self.designInfo,
          () => {
            nextTick(() => self.initSortable())
          },
          { deep: true, immediate: true }
        )

        return resutl
      },
      mounted() {
        // self.mountComCss()
        this.$nextTick(() => {
          self.initSortable()
        })
      },
      methods: {},
      render(this: BwVueIns) {
        self.curVueIns = this
        const nodes = self.initComNodes(this)

        return h(
          designContainer,
          {
            id: 'curtom-com-design-canvas',
          },
          () => (Array.isArray(nodes) ? nodes : [])
        )
      },
    })
    return curCom
  }

  /** 生成实例组件外放属性 */
  protected createComProps() {
    const curProps: Record<string, any> = {}
    const comProps = this.designInfo?.comProps
    if (Array.isArray(comProps) && comProps.length) {
      comProps.forEach((n) => {
        const name = n.key
        const dataType = n.dataType
        if (name && Array.isArray(dataType) && dataType.length) {
          const { defaultValue, defaultValueDataType } = n
          curProps[name] = {
            require: !!n.isRequire,
            type: dataType.map((n) => getCustomComPropDataType(n)),
            default: getCustomComPropDefaultValue(
              defaultValueDataType ?? undefined,
              defaultValue ?? undefined
            ),
          }
        }
      })
    }
    return curProps
  }

  /** 生成实例组件外放事件 */
  protected createComEmits() {
    const emitList: Array<string> = []
    const emits = this.designInfo?.emits
    if (Array.isArray(emits) && emits.length) {
      emits.forEach((n) => emitList.push(n.key))
    }
    return emitList
  }

  /** 动态加载自定义逻辑 */
  protected getSetupLogic() {
    const curInfo = this.designInfo
    const curSetup = curInfo?.setup ?? {}
    return curSetup[this.frontType]
  }

  /** 挂载全局样式 */
  protected mountComCss() {
    const curCss = this.designInfo?.comCss
    if (curCss) {
      const curFrontCss = curCss[this.frontType] ?? ''
      addStyleToHead('my-com-crate-css', curFrontCss)
    }
  }

  /** 初始化组件拖拽 */
  protected initSortable() {
    const structs = this.designInfo?.structs
    if (isObject(structs)) {
      const ele = document.getElementById(`curtom-com-design-canvas`)
      if (ele) {
        useSortable(ele)
      }
    }
  }

  /** 初始化组件节点 */
  protected initComNodes(vueIns: BwVueIns) {
    const structs = this.designInfo?.structs
    if (isObject(structs)) {
      const curFrontStructs = structs[this.frontType] ?? []
      const nodes = this.getNodeEleRenderInfo(vueIns, curFrontStructs, {})
      this.curComNodes = nodes
      return nodes
    } else {
      this.curComNodes = []
      return null
    }
  }

  /**
   * 获取组件Render信息
   * @param vueIns                  vue 实例
   * @param structs                 Dom树
   * @param scopeData               插槽数据
   */
  protected getNodeEleRenderInfo(
    vueIns: BwVueIns,
    structs: Array<BwCustomComStructModel>,
    scopeData: Record<string, any>
  ): VNode[] {
    let nodes: VNode[] = []
    if (Array.isArray(structs)) {
      structs.forEach((cDom) => {
        const nodeList = this.getNodeEleComInfo(vueIns, cDom, scopeData)
        nodes = nodes.concat(nodeList)
      })
    } else {
      nodes.push(h('span', {}, '还未设计元素节点'))
    }
    return nodes
  }

  /**
   * 节点组件渲染
   * @param vueIns                  vue 实例
   * @param struct                  Dom信息
   * @param scopeData               插槽数据
   */
  protected getNodeEleComInfo(
    vueIns: BwVueIns,
    struct: BwCustomComStructModel,
    scopeData: Record<string, any>
  ): VNode[] {
    const nodes: Array<VNode> = []
    const curScopeData = scopeData
    const { nodeId, children } = struct || {}

    /** 判断当前节点是否是条件节点，且条件允许渲染 */
    const nodeComInfo = h(
      markRaw(desiginItem),
      {
        vueIns,
        ref: nodeId,
        key: nodeId,
        nodeInfo: struct,
        frontType: this.frontType,
        scopeData: curScopeData,
      },
      this.getChildNodeEleComInfo(vueIns, children ?? null, curScopeData)
    )
    nodes.push(nodeComInfo)

    return nodes
  }

  /**
   * 获取子节点
   * @param vueIns
   * @param comId
   * @param slotList
   * @param domNodes
   */
  protected getChildNodeEleComInfo(
    vueIns: BwVueIns,
    domNodes: Nullable<Record<string, Array<BwCustomComStructModel>>>,
    scopeData: Record<string, any>
  ): Record<string, BwChildrenNodeCallback> {
    const self = this
    const resData: Record<string, BwChildrenNodeCallback> = {}
    if (domNodes) {
      Object.keys(domNodes).forEach((slotName) => {
        const childNodes = domNodes ? domNodes[slotName] : []

        resData[slotName] = (slotInfos: any) => {
          Object.assign(scopeData, slotInfos || {})
          let nodes: VNode[] = []
          if (childNodes && childNodes.length) {
            nodes = self.getNodeEleRenderInfo(vueIns, childNodes, scopeData)
          }

          return nodes
        }
      })
    }
    return resData
  }

  /** 设置设计运行端 */
  public setFrontType(type: BwCustomComFrontTypeEnum) {
    this.frontType = type
  }

  /** 设置设计信息 */
  public setDesignInfo(info: BwCustomComDesignInfoModel) {
    this.curCustomCom = null
    this.designInfo = info
  }

  /** 获取自定义组件 */
  public getCom() {
    if (this.curCustomCom == null) {
      //@ts-ignore
      this.curCustomCom = this.createCom()
    }
    return this.curCustomCom
  }

  /** 刷新自定义组件 */
  public refreshCom() {
    //@ts-ignore
    this.curCustomCom = this.createCom()
    return this.curCustomCom
  }

  /** 更新元素节点 */
  public onUpdateNodeInfo(nodeId: string) {
    const refs = this.curVueIns?.$refs
    if (refs) {
      const insEle: any = refs[nodeId]
      const setNodeInfo = insEle?.setNodeInfo
      if (isFunction(setNodeInfo)) {
        setNodeInfo(nodeId)
      }
    }
  }

  /** 隐藏元素节点 */
  public onHideNodeEle(nodeIds: Array<string>) {
    const curHideNodeIds: Array<string> = []
    const nodes = document.querySelectorAll(`.bw-com-node-hide`)

    nodes.forEach((ele) => {
      const eleNodeId = ele.getAttribute('nodeid') ?? ''

      if (nodeIds.includes(eleNodeId)) {
        curHideNodeIds.push(eleNodeId)
        ele?.classList.add('bw-com-node-hide')
      } else {
        ele?.classList.remove('bw-com-node-hide')
      }
    })
    if (nodeIds.length) {
      nodeIds.forEach((nodeId) => {
        if (!curHideNodeIds.includes(nodeId)) {
          const domId = `com_${nodeId}`
          const node = document.getElementById(domId)
          if (node) {
            node.classList.add('bw-com-node-hide')
          }
        }
      })
    }
  }

  /** 选中DOM节点 */
  public onSelectNode(nodeId: string) {
    const domId = `com_${nodeId}`
    const node = document.getElementById(domId)
    const nodes = document.querySelectorAll(`.bw-design-node-select`) ?? []

    if (node) {
      const classList = node.classList
      nodes.forEach((ele) => {
        const id = ele.getAttribute('id')
        if (domId !== id) {
          ele.classList.remove('bw-design-node-select')
        }
      })

      if (!classList.value.split(' ').includes('bw-design-node-select')) {
        node.classList.add('bw-design-node-select')
      }
    }
  }
}
